3-3 Fiber树与双缓存技术(任务优先级)
Fiber 的树形结构
Fiber 对象通过三个关键属性建立起树形结构:
| 属性 | 方向 | 说明 |
|---|---|---|
child | 父 → 第一个子节点 | 指向第一个子 Fiber |
sibling | 兄弟 → 兄弟 | 指向下一个兄弟 Fiber |
return | 子 → 父 | 指向父 Fiber(即 return 回去的节点) |
这和虚拟 DOM 的树形结构非常类似,用于描述工作之间的关联关系和执行顺序。一个重要的原则是:所有单一节点的 return 最终都会指向根节点。
假设有如下 DOM 结构:
<div>
<h1></h1>
<h2></h2>
<h3></h3>
</div>
Fiber 树结构:
div (根节点)
├── h1 (child of div, sibling → h2)
├── h2 (sibling of h1, sibling → h3)
└── h3 (sibling of h2)
每个节点的 return 都指向 div
text
任务分类与优先级
Fiber 将工作分为两类:
1. 立即执行的工作(高优先级)
用户直接感知的任务,延迟会导致明显的体验问题:
- 动画帧更新
- 用户输入响应
- 交互反馈(如按钮点击效果)
2. 待办清单(低优先级)
可以延迟执行的任务,不影响用户的即时体验:
- 网络请求的数据处理
- 非关键状态的更新
- 长列表的分批渲染
时间切片技术
Fiber 利用时间切片(Time Slicing)将任务拆分到不同的帧中执行。每帧被分成两个阶段:
- 逻辑处理阶段——执行高优先级的计算任务
- 提交渲染阶段——将变更同步到 DOM
这就像长列表的分页加载:先渲染用户当前可见的部分,等主线程空闲后再处理剩余部分。
两个核心浏览器 API
| API | 用途 | 兼容性 |
|---|---|---|
requestAnimationFrame | 高优先级任务,与浏览器刷新率同步(通常 60Hz) | 非常好,IE10+ |
requestIdleCallback | 低优先级任务,在浏览器空闲时执行 | 一般,IE 不支持 |
requestAnimationFrame常用于动画组件,因为它直接与视图层绑定,确保动画帧率流畅requestIdleCallback用于执行不紧急的任务,React 在不支持该 API 的浏览器中会使用 polyfill
双缓存技术(Double Buffering)
双缓存是 Fiber 架构中最精妙的设计之一。React 在内存中同时维护两棵 Fiber 树:
Current Tree(当前树)
当前屏幕上正在显示的内容对应的 Fiber 树。
WorkInProgress Tree(工作树)
正在内存中构建的、即将要显示的下一帧内容对应的 Fiber 树。
双缓存工作流程:
1. 用户看到的内容 → Current Tree
2. React 在内存中准备 → WorkInProgress Tree
3. WorkInProgress Tree 所有工作完成
4. 切换:WorkInProgress → Current(原子操作)
5. 旧的 Current → 变为新的 WorkInProgress(等待下次复用)
text
两棵树通过 Fiber 对象的 alternate 属性相互连接:
currentFiber.alternate === workInProgressFiber;
workInProgressFiber.alternate === currentFiber;
javascript
alternate 是一个连接器,指向对立状态的 Fiber,帮助 Fiber 进行缓存与复用。这种设计类似于代码中常见的 parent/children 双向引用模式,在递归和嵌套算法中非常有用。
双缓存的优势
| 优势 | 说明 |
|---|---|
| 零闪烁 | 新内容在内存中完全构建好后再一次性切换,用户不会看到中间状态 |
| 高效复用 | 旧的 Fiber 树不会销毁,而是作为下次更新的 WorkInProgress 复用 |
| 可中断恢复 | WorkInProgress 的构建过程可以中断,恢复后从中断点继续 |
| 原子切换 | 指针切换是原子操作,不存在半完成状态 |
Fiber 树的遍历顺序
Fiber 树的执行遵循深度优先遍历(DFS),通过 child → sibling → return 的路径完成:
A1 (根节点)
├── B1 (child of A1)
├── B2 (sibling of B1)
│ └── C1 (child of B2)
│ ├── D1 (child of C1)
│ └── D2 (sibling of D1)
└── B3 (sibling of B2)
执行顺序:A1 → B1 → B2 → C1 → D1 → D2 → C1(完成) → B2(完成) → B3 → A1(完成)
text
具体规则:
- 从根节点 A1 开始
- 有 child 就往下(B1)
- B1 无 child,执行完成
- 有 sibling 就往右(B2)
- B2 有 child(C1),继续往下
- C1 有 child(D1),继续往下
- D1 无 child,执行完成
- D1 有 sibling(D2),执行 D2
- D2 无 child,执行完成
- C1 的所有 child 完成,C1 完成
- B2 的所有 child 完成,B2 完成
- B2 有 sibling(B3),执行 B3
- 所有 child 和 sibling 完成,A1 完成
这个遍历过程是异步的、可中断的——这就是 Fiber 架构能够实现优先级调度和可中断渲染的底层机制。
↑